//
// connection.cpp
// ~~~~~~~~~~~~~~
//

#include "connection.hpp"
#include <vector>
#include <boost/bind.hpp>
#include "request_handler.hpp"
#include "server.hpp"
namespace http {
    namespace server3 {

        int connection::connection_num =  0;
        int connection::request_num = 0;

        connection::connection(boost::asio::io_service& io_service, request_handler& handler)
            : socket_(io_service),
            strand_(io_service),
            request_handler_(handler),
            time_out(__DEFAULT_CONNECTION_TIMEOUT),
            timer_(io_service)
        {
        }
        boost::asio::ip::tcp::socket& connection::socket()
        {
            return socket_;
        }

        void connection::start()
        {
        	__sync_fetch_and_add(&connection_num,1);
            boost::system::error_code ec;
            boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(ec);
            if (ec)
            {
            	server::log_business_count_impl("ERROR_IP");
            	port = 0;
            	ip = "0.0.0.0";
            }else{
                port = endpoint.port();
                ip = endpoint.address().to_string();
                ip.erase(std::remove(ip.begin(), ip.end(),' '), ip.end());
            }

            //request_ptr_.reset(new request);
            reset_new_request();
            socket_.async_read_some(boost::asio::buffer(buffer_),
                boost::bind(&connection::handle_read, shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));

            /// shutdown connection if there's not request after timeout.
            timer_.expires_from_now(boost::posix_time::seconds(time_out));
            timer_.async_wait(
                boost::bind(&connection::handle_stop, shared_from_this(),
                boost::asio::placeholders::error));
        }

        void connection::handle_read(const boost::system::error_code& e,
            std::size_t bytes_transferred)
        {
            if (!e)
            {
                request_parser::parse_state result = request_parser_.parse(
                    *request_ptr_.get(), buffer_.data(), buffer_.data() + bytes_transferred);

                if (result == request_parser::parse_completed)
                {
                    //create a new reply
                    reply_ptr_.reset(new reply());
                    //request_ptr_->parse_uri_parameter();

                    //for statistics
                    __sync_fetch_and_add(&request_num,1);

                    std::string con_state;
                   if(request_ptr_->get_header_value("Connection",con_state)){
                       if( con_state.find("close")!=string::npos){
                    	   //header Connection contains "Close"
                    	   connection_close = true;
                    	   reply_ptr_->add_header("Connection","close");
                       }else{
                    	   //"keep-alive" or other options
                    	   connection_close  = false;
                       }
                   }else if (request_ptr_->http_version_minor == 0){
                	   //http 1.0
                	   connection_close = true;
                	   reply_ptr_->add_header("Connection","close");
                   }else{
                	   //http 1.1
                	   connection_close = false;
                   }
                    request_handler_.handle_request(request_ptr_, reply_ptr_);
                    boost::asio::async_write(socket_, reply_ptr_->to_buffers(),
                        boost::bind(&connection::handle_write, shared_from_this(),
                        boost::asio::placeholders::error));
                }
                else if (result == request_parser::parse_not_completed)
                {
        	        //__sync_fetch_and_add(&server::parsing_error,1);
                    socket_.async_read_some(boost::asio::buffer(buffer_),
                        boost::bind(&connection::handle_read, shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
                }
                else
                {
                	__sync_fetch_and_add(&server::parsing_error,1);
                    reply_ptr_ = reply::stock_reply(reply::bad_request);
                    boost::asio::async_write(socket_, reply_ptr_->to_buffers(),
                        boost::bind(&connection::handle_write, shared_from_this(),
                        boost::asio::placeholders::error));
                }
            }else if (e == boost::asio::error::eof)
            {
            }else{
            	server::log_business_count_impl("ERROR_READ");
            	stop();
            }
        }

        void connection::handle_write(const boost::system::error_code& e)
        {
            if (!e)
            {
                reset_new_request();
                socket_.async_read_some(boost::asio::buffer(buffer_),
                    boost::bind(&connection::handle_read, shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
                if(connection_close){
                	stop();
                }else{
					// cancel timer
                    timer_.expires_from_now(boost::posix_time::seconds(time_out));
                    timer_.async_wait(
                        boost::bind(&connection::handle_stop, shared_from_this(),
                        boost::asio::placeholders::error));
                }
            }else
            {
            	server::log_business_count_impl("ERROR_WRITE");
                stop();
            }

        }

        void connection::handle_stop(const boost::system::error_code& e)
        {
            // Check whether the deadline has passed. We compare the deadline against
            // the current time since a new asynchronous operation may have moved the
            // deadline before this actor had a chance to run.
            if(e == boost::asio::error::operation_aborted)
            {
            }else
            {
                // The deadline has passed. The socket is closed so that any outstanding
                // asynchronous operations are canceled.
                stop();
            }
        }

        void connection::stop()
        {
           // connection_num--;
        	__sync_fetch_and_sub(&connection_num,1);
            boost::system::error_code ignored_ec;
            socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
            if (ignored_ec)
            {
            	server::log_business_count_impl("ERROR_SHUTDOW_SOCKET");
            }else
            {
            	boost::system::error_code ec;
            	socket_.close(ec);
            	if (ec)
            	{
            		server::log_business_count_impl("ERROR_CLOSE_SOCKET");
            	}
            }
            // No new asynchronous operations are started. This means that all shared_ptr
            // references to the connection object will disappear and the object will be
            // destroyed automatically after this handler returns. The connection class's
            // destructor closes the socket.
        }

        void connection::reset_new_request()
        {
            request_ptr_.reset(new request);
            request_parser_.reset();
            request_ptr_->port = this->port;
            request_ptr_->ip = this->ip;
        }
    } // namespace server2
} // namespace http
